package flib;

import importExport.SVGLoader;
import importExport.SVGText;
import importExport.XmlInOut;

import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.io.File;
import java.util.HashMap;
import java.util.Vector;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;


import simpleGeom.HasPos2D;
import simpleGeom.HasPos3D;
import simpleGeom.Polygon2D;
import simpleGeom.Vec2D;
import simpleGeom.Vec3D;

public class FloorplanLoader {

	HashMap<String, WallType> walltypes;
	HashMap<String, RoomType> roomtypes;
	HashMap<Stair, Vector<FlibWall>> stairPaths = new HashMap<Stair, Vector<FlibWall>>();
	Plan plan;
	
	public void loadWallTypes(File file) {
		walltypes = new HashMap<String, WallType>();
		Document doc = XmlInOut.loadDoc(file);
		NodeList xmlWalltypes = doc.getElementsByTagName("wallType");
		for (int i = 0; i < xmlWalltypes.getLength(); i++) {
			Element xmlWallType = (Element) xmlWalltypes.item(i);
			WallType type = new WallType();
			type.load(xmlWallType);
			walltypes.put(type.label, type);
		}
	}

	public void loadRoomTypes(File file) {
		roomtypes = new HashMap<String, RoomType>();
		Document doc = XmlInOut.loadDoc(file);
		NodeList xmlRoomtypes = doc.getElementsByTagName("roomType");
		for (int i = 0; i < xmlRoomtypes.getLength(); i++) {
			Element xmlRoomType = (Element) xmlRoomtypes.item(i);
			RoomType type = new RoomType();
			type.load(xmlRoomType);
			roomtypes.put(type.label, type);
		}
	}

	public Plan loadFloorplan(File file) {
		plan=new Plan();
		plan.roomtypes=roomtypes;
		plan.walltypes=walltypes;
		Document doc = XmlInOut.loadDoc(file);
		NodeList xmlLayers = doc.getElementsByTagName("layer");
		Vector<Element> layers = new Vector<Element>();
		for (int i = 0; i < xmlLayers.getLength(); i++) {
			Element xmlLayer = (Element) xmlLayers.item(i);
			String elName = xmlLayer.getAttribute("name");
			if (elName.startsWith("Konstruktionsebene")) {
				layers.add(xmlLayer);
			}
		}
		for (int i = 0; i < layers.size(); i++) {
			Element xmlLayer = (Element) layers.get(i);
			String elName = xmlLayer.getAttribute("name");
			FlibLevel group = new FlibLevel();
			int iFloor = plan.levels.size();
			group.z = iFloor * 3;
			plan.levels.add(group);
			String[] nameSegments = elName.split("-");

			int floor = Integer.valueOf(nameSegments[nameSegments.length - 1]);
			NodeList xmlPolys = xmlLayer.getElementsByTagName("polygon");

			for (int ii = 0; ii < xmlPolys.getLength(); ii++) {
				Element xmlPoly = (Element) xmlPolys.item(ii);
				Vector<Point2D> poly = SVGLoader.getPolygon(xmlPoly);
				String type = xmlPoly.getAttribute("class");
				if (type.equals("room-stair")) {
					Stair stair = new Stair();
					for (int j = 0; j < poly.size(); j++) {
						Point2D p = poly.get(j);
						stair.addNode((float) p.getX(), (float) p.getY(), 0.01f);
					}
					for (int j = 0; j < stair.nodes.size(); j++) {
						FlibHENode n1 = stair.nodes.get(j);
						int jj = (j + 1) % stair.nodes.size();
						FlibHENode n2 = stair.nodes.get(jj);
						FlibWall wall = new FlibWall(n1, n2);
						wall.type = walltypes.get("wall-balustrade");
						stair.walls.add(wall);
					}
					RoomType roomType = roomtypes.get(type);
					stair.parent = group;
					Vector<FlibWall> stairPath = new Vector<FlibWall>();
					stairPaths.put(stair, stairPath);
					group.stairs.add(stair);
				}
			}
			NodeList xmlLines = xmlLayer.getElementsByTagName("line");
			for (int ii = 0; ii < xmlLines.getLength(); ii++) {
				Element xmlLine = (Element) xmlLines.item(ii);
				Line2D line = SVGLoader.getLine2D(xmlLine);
				String type = xmlLine.getAttribute("class");
				WallType wallType = walltypes.get(type);
				if (type.equals("stair-segment")) {
					for (int j = 0; j < group.stairs.size(); j++) {
						Stair stair = group.stairs.get(j);
						if (stair.contains2D((float) line.getX1(),
								(float) line.getY1(), (float) line.getX2(),
								(float) line.getY2(), 0.01f)) {
							FlibWall wall = stair.addWall((float) line.getX1(),
									(float) line.getY1(), (float) line.getX2(),
									(float) line.getY2(), 0.01f);
							wall.type = wallType;
						}
					}
				} else if (type.equals("stair-path")) {
					FlibHENode n1 = new FlibHENode((float) line.getX1(),
							(float) line.getY1());
					FlibHENode n2 = new FlibHENode((float) line.getX2(),
							(float) line.getY2());
					FlibWall wall = new FlibWall(n1, n2);
					wall.type = wallType;
					for (int j = 0; j < group.stairs.size(); j++) {
						Stair stair = group.stairs.get(j);
						Vector<FlibWall> stairpath = stairPaths.get(stair);
						if (Polygon2D.contains(wall.cX(), wall.cY(), stair.nodes)) {
							stairpath.add(wall);
						}
					}
				} else {
					FlibWall wall = group.addWall((float) line.getX1(),
							(float) line.getY1(), (float) line.getX2(),
							(float) line.getY2(), 0.02f);
					wall.type = wallType;
				}
			}
			group.createGraph();
			group.createRooms();
			for (int ii = 0; ii < group.nodes.size(); ii++) {
				FlibHENode n = group.nodes.get(ii);
				n.z = iFloor * 3;
			}
		}
		for (int i = 0; i < plan.levels.size() - 1; i++) {
			FlibLevel group1 = plan.levels.get(i);
			FlibLevel group2 = plan.levels.get(i + 1);
			group1.upGroup = group2;
			group2.downGroup = group1;
		}
		for (int i = 0; i < plan.levels.size(); i++) {
			FlibLevel group = plan.levels.get(i);
			for (int j = 0; j < group.stairs.size(); j++) {
				Stair stair = group.stairs.get(j);
				construct(stair);
				group.walls.addAll(stair.walls);
				group.rooms.addAll(stair.rooms);
				setHeight(stair);
			}
		}
		
		
		for (int i=0;i<plan.levels.size();i++){
			FlibLevel level=plan.levels.get(i);
			plan.nodes.addAll(level.nodes);
			plan.walls.addAll(level.walls);
			plan.rooms.addAll(level.rooms);
		}
		
		return plan;
	}

	public void construct(Stair stair) {
		Vector<FlibWall> stairstartWalls = stair.parent
				.getWallsByType("stair-start");
		Vector<FlibWall> stairendWalls = stair.parent.getWallsByType("stair-end");
		FlibWall startWall;
		FlibWall endWall;
		if (stair.parent.upGroup != null) {
			stairendWalls = stair.parent.upGroup.getWallsByType("stair-end");
		}
		Room room = stair.parent.findRooms(stair.nodes, 0.1f);
		if (room != null) {
			stair.parent.removeRoom(room);
		}
		for (int jj = 0; jj < stairstartWalls.size(); jj++) {
			FlibWall wall = stairstartWalls.get(jj);
			if (stair.contains2D(wall.x1(), wall.y1(), wall.x2(), wall.y2(),
					0.01f)) {
				startWall = wall;
				stair.replaceWall(startWall, 0.01f);
			}
		}

		for (int jj = 0; jj < stairendWalls.size(); jj++) {
			FlibWall wall = stairendWalls.get(jj);

			if (stair.contains2D(wall.x1(), wall.y1(), wall.x2(), wall.y2(),
					0.01f)) {
				if (stair.parent.upGroup == null) {
					FlibHENode n1 = new FlibHENode(wall.x1(), wall.y1(),
							stair.parent.z + 3);
					FlibHENode n2 = new FlibHENode(wall.x2(), wall.y2(),
							stair.parent.z + 3);
					wall = new FlibWall(n1, n2);
					wall.type = walltypes.get("stair-end");
				}
				endWall = wall;

				stair.replaceWall(endWall, 0.01f);

			}
		}

		Vector<FlibWall> stairSegments = stair.getWallsByType("stair-segment");

		for (int ii = 0; ii < stairSegments.size(); ii++) {
			FlibWall wall = stairSegments.get(ii);
			Vec3D v = Vec3D.sub(wall.getN2(), wall.getN1());
			v.setLength(0.1f);
			wall.getN1().add(v);
			wall.getN2().sub(v);
		}

		for (int ii = 0; ii < stair.walls.size(); ii++) {
			FlibWall wall = stair.walls.get(ii);
			wall.hEdge.attach();
		}

		for (int ii = 0; ii < stairSegments.size(); ii++) {
			FlibWall wall = stairSegments.get(ii);
			Vec3D v = Vec3D.sub(wall.getN2(), wall.getN1());
			v.setLength(0.1f);
			wall.getN1().sub(v);
			wall.getN2().add(v);
			if (wall.hEdge.getFace() == null) {
				Room croom = new Room();
				croom.attach(wall.hEdge);
				stair.rooms.add(croom);
			}
			if (wall.hEdge.getSym().getFace() == null) {
				Room croom = new Room();
				croom.attach(wall.hEdge.getSym());
				stair.rooms.add(croom);
			}
		}
	}

	public void setHeight(Stair stair) {
		FlibLevel parent = stair.parent;
		Vector<Room> rooms = stair.rooms;
		float z0 = parent.z;
		float z1 = z0 + 3;
		if (parent.upGroup != null) {
			z1 = parent.upGroup.z;
		}
		float sumL = 0;
		Vector<FlibWall> stairPath = stairPaths.get(stair);

		for (int i = 0; i < stairPath.size(); i++) {
			FlibWall wall = stairPath.get(i);
			sumL += Vec2D.dist(wall.getNode1(), wall.getNode2());
		}
		for (int i = 0; i < rooms.size(); i++) {
			Room r = rooms.get(i);
			for (int j = 0; j < stairPath.size(); j++) {
				FlibWall w = stairPath.get(j);
				if (r.contains(w.cX(), w.cY())) {
					r.setSteps(true);
				}
			}
		}
		FlibWall startLine = stair.getWallsByType("stair-start").firstElement();
		Room startFace = (Room) startLine.hEdge.getFace();
		if (!rooms.contains(startFace)) {
			startFace = (Room) startLine.hEdge.getSym().getFace();
		}
		Vector<Room> sequence = new Vector<Room>();
		Room nextRoom = startFace;
		sequence.add(nextRoom);
		while (nextRoom != null) {
			Vector<FlibFace> nbs = nextRoom.getNbs();
			nextRoom = null;
			for (int i = 0; i < nbs.size(); i++) {
				Room r = (Room) nbs.get(i);
				if (rooms.contains(r) && !sequence.contains(r)) {
					nextRoom = r;
					sequence.add(nextRoom);
				}
			}
		}
		float cH = z0;
		Vector<FlibWall> stairSegments = stair.getWallsByType("stair-segment");
		FlibWall endLine = stair.getWallsByType("stair-end").firstElement();
		for (int i = 0; i < sequence.size(); i++) {
			Room r1 = sequence.get(i);
			if (r1.isSteps()) {
				FlibWall firstWall = startLine;
				FlibWall secondWall = endLine;
				if (i > 0) {
					Room r0 = sequence.get(i - 1);
					for (int j = 0; j < stairSegments.size(); j++) {
						FlibWall w = stairSegments.get(j);
						Room room1 = (Room) w.hEdge.getFace();
						Room room2 = (Room) w.hEdge.getSym().getFace();
						if (room1 == r0 && room2 == r1 || room2 == r0
								&& room1 == r1) {
							firstWall = w;
						}
					}
				}
				if (i < sequence.size() - 1) {
					Room r2 = sequence.get(i + 1);
					for (int j = 0; j < stairSegments.size(); j++) {
						FlibWall w = stairSegments.get(j);
						Room room1 = (Room) w.hEdge.getFace();
						Room room2 = (Room) w.hEdge.getSym().getFace();
						if (i < sequence.size() - 1
								&& (room1 == r2 && room2 == r1 || room2 == r2
										&& room1 == r1)) {
							secondWall = w;
						}
					}
				}
				float cD = Vec2D.dist(firstWall.cX(), firstWall.cY(),
						secondWall.cX(), secondWall.cY());
				float dZ = (z1 - z0) * cD / sumL;
				cH += dZ;
				secondWall.getN1().z = cH;
				secondWall.getN2().z = cH;
			} else {
				Vector<FlibHENode> nodes = r1.getFaceNodes();
				for (int j = 0; j < nodes.size(); j++) {
					FlibHENode n = (FlibHENode) nodes.get(j);
					n.z = cH;
				}
			}
		}
	}
}
